package com.crb.crm.manage.excel.listener;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.BaseRowModel;
import com.crb.crm.manage.utils.JSONUtils;
import com.crb.crm.manage.excel.ExcelTemplate;
import com.crb.crm.manage.utils.ClassUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Package: com.crb.crm.manage.excel.listener
 * Description：
 * Author: huangjian
 * Date:  2020/9/5 17:18
 * Modified By:
 *
 * @author huangjian
 */
@Slf4j
public abstract class BaseExcelListener<T extends BaseRowModel> extends AnalysisEventListener<T> {
    /**
     * 批处理阈值
     */
    private static final int BATCH_COUNT = 5;
    List<T> list = new ArrayList<>(BATCH_COUNT);
    List<T> errorList = Lists.newArrayList();

    private Integer totalCount = 0;

    @SneakyThrows
    @Override
    public void invoke(T model, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", JSONUtils.obj2json(model));
        totalCount++;
        ValidResult valid = new ValidResult();
        this.validData(model, valid);
        if (valid.isSuccess()) {
            list.add(model);
            if (list.size() >= BATCH_COUNT) {
                log.info("{}条数据，开始存储数据库！", list.size());
                this.saveData(list);
                log.info("存储数据库成功！");
                list.clear();
            }
        } else {
            Class aClass = rowModelClass();
            Field errors = aClass.getDeclaredField("errors");
            errors.setAccessible(true);
            errors.set(model, valid.formatErrors());
            errorList.add(model);
        }
    }

    /**
     * 表头校验
     *
     * @param headMap
     * @param context
     */
    @SneakyThrows
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        list.clear();
        totalCount = 0;
        errorList.clear();
        super.invokeHeadMap(headMap, context);
        Class aClass = rowModelClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        Map<Integer, String> realHeadMap = Maps.newHashMap();
        Stream.of(declaredFields).filter(f -> f.isAnnotationPresent(ExcelProperty.class) && !"errors".equals(f.getName()))
                .forEach(f -> {
                    ExcelProperty annotation = f.getAnnotation(ExcelProperty.class);
                    realHeadMap.put(annotation.index(), annotation.value()[0]);
                });
        realHeadMap.keySet().forEach(index -> {
            if (!realHeadMap.get(index).equals(headMap.get(index))) {
                throw new IllegalArgumentException("解析Excel出错,请传入正确模板的Excel");
            }
        });
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
       if(!CollectionUtils.isEmpty(list)){
           saveData(list);
       }
        log.info("所有数据解析完成！");
    }

    public List<T> getErrorList() {
        return errorList;
    }

    public Integer getTotalCount() {
        return totalCount;
    }

    public Class rowModelClass() {
        return ClassUtils.getClassT(this, 0);
    }

    /**
     * 校验单行数据是否正确
     *
     * @param t
     * @return
     */
    protected abstract void validData(T t, ValidResult validResult);

    @Transactional(rollbackFor = Exception.class)
    protected abstract void saveData(List<T> list);

    protected abstract ExcelTemplate support();

    protected static class ValidResult {
        private List<String> messages = Lists.newArrayList();

        public void addError(String message) {
            this.messages.add(message);
        }

        private String formatErrors() {
            return messages.stream().collect(Collectors.joining(";"));
        }

        private boolean isSuccess() {
            return CollectionUtils.isEmpty(messages);
        }
    }
}
